﻿Imports System.ComponentModel
Imports System.Text
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq

Public Class FrmMain
    Private JsonList As New JArray
    Private JsonIndex(0) As Integer '用来记录项目的实际位置

    Private Structure ListItemType
        Public Icon As Image
        Public Title As String
        Public Content As String
    End Structure

    Private ListData(0) As ListItemType
    Private ListNum As Integer = 0
    Private Const ItemHeight As Integer = 42

    Private Sub FrmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim IsRun As Boolean = False
        Dim mtx As New Threading.Mutex(True, Application.ProductName, IsRun)
        If Not IsRun Then End  '防止重复运行

        Width = 650
        PalSearch.Size = New Size(ClientSize.Width - 20, TxtSearch.Height + 10)
        PicIcon.Location = New Point(PalSearch.Height / 2 - PicIcon.Height / 2, PalSearch.Height / 2 - PicIcon.Height / 2)
        TxtSearch.Location = New Point(PalSearch.Height, 5)
        TxtSearch.Width = PalSearch.Width - PalSearch.Height * 2
        PicClean.Location = New Point(TxtSearch.Right + PalSearch.Height / 2 - PicClean.Height / 2, PalSearch.Height / 2 - PicClean.Height / 2)
        ListBox.Size = New Size(ClientSize.Width - 20, 0)
        ListBox.Location = New Point(10, PalSearch.Top + PalSearch.Height + 10)
        ListBox.Visible = False

        Height = Height - ClientSize.Height + PalSearch.Height + 20
        Left = Screen.PrimaryScreen.Bounds.Width / 2 - Width / 2
        Top = Screen.PrimaryScreen.Bounds.Height / 3 - (Height + PalSearch.Height * 6) / 2

        Text = AppName + " " + AppVersion
        NotifyIcon.Icon = Icon
        NotifyIcon.Text = AppName

        If True Then
            FormBorderStyle = FormBorderStyle.FixedSingle
        Else
            FormBorderStyle = FormBorderStyle.None
        End If

        If IO.File.Exists(ConfigPath) = False Then
            Dim f As String
            Dim l As JObject
            Dim a As JArray = New JArray
            Dim i As Integer
            f = Encoding.GetEncoding("utf-8").GetString(My.Resources.config)
            ConfigData = CType(JsonConvert.DeserializeObject(f), JObject)
            f = Encoding.GetEncoding("utf-8").GetString(My.Resources.command)
            l = CType(JsonConvert.DeserializeObject(f), JObject)
            For i = 0 To l("app").Count - 1
                a.Add(l("app")(i))
            Next
            For i = 0 To l("sys").Count - 1
                a.Add(l("sys")(i))
            Next
            ConfigData("list") = a
            IO.File.WriteAllText(ConfigPath, JsonInfo + ConfigData.ToString, Encoding.UTF8)
        Else
            ConfigData = CType(JsonConvert.DeserializeObject(IO.File.ReadAllText(ConfigPath)), JObject)
        End If

        Dim hotkey() As String
        hotkey = Split(Replace(ConfigData("setting")("hotkey").ToString, " ", ""), "+")
        If hotkey.Count = 2 Then
            If RegisterHotKey(Handle, 1, GetKey(hotkey(0), True), GetKey(hotkey(1))) = False Then
                MsgBox("注册全局热键（" + hotkey(0) + "+" + hotkey(1) + "）失败，可能已被其他程序占用。", vbOKOnly + vbExclamation, "警告")
            End If
        ElseIf hotkey.Count = 3 Then
            If RegisterHotKey(Handle, 1, GetKey(hotkey(0), True) + GetKey(hotkey(1), True), GetKey(hotkey(2))) = False Then
                MsgBox("注册全局热键（" + hotkey(0) + "+" + hotkey(1) + "+" + hotkey(2) + "）失败，可能已被其他程序占用。", vbOKOnly + vbExclamation, "警告")
            End If
        Else
            MsgBox("从配置文件读取快捷键数据失败，请检查配置文件格式是否正确。", vbOKOnly + vbExclamation, "错误")
            MenuExit_Click(Me, New Object)
        End If

        SetThemes()

        If CType(ConfigData("setting")("bar"), Boolean) = False Then
            FormBorderStyle = FormBorderStyle.None
        End If

        Opacity = CType(ConfigData("setting")("transparency"), Integer) / 100

        If CType(ConfigData("setting")("update"), Boolean) Then
            UpdateDLL.CheckUpdate(AppUpdateTime, AppUpdateUrl)
        End If
    End Sub

    Private Sub FrmMain_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
        Visible = False
        e.Cancel = True
    End Sub

    Private Sub FrmMain_Shown(sender As Object, e As EventArgs) Handles Me.Shown
        Visible = False
    End Sub

    'Private Sub FrmMain_LostFocus(sender As Object, e As EventArgs) Handles Me.LostFocus
    '    Visible = False
    'End Sub

    Private Sub PicClean_Click(sender As Object, e As EventArgs) Handles PicClean.Click
        TxtSearch.Text = ""
        PicClean.Visible = False
        If CType(ConfigData("setting")("showall"), Boolean) Then
            AllList()
        Else
            ListBox.Visible = False
            Height = Height - ClientSize.Height + PalSearch.Height + 20
        End If
    End Sub

    Private Sub PicClean_MouseEnter(sender As Object, e As EventArgs) Handles PicClean.MouseEnter
        PicClean.BackgroundImage = My.Resources.picClean2
    End Sub

    Private Sub PicClean_MouseLeave(sender As Object, e As EventArgs) Handles PicClean.MouseLeave
        PicClean.BackgroundImage = My.Resources.picClean1
    End Sub

    Private Sub MenuOpenWindow_Click(sender As Object, e As EventArgs) Handles MenuOpenWindow.Click
        If ConfigData("setting")("clear") Then
            TxtSearch.Text = ""
            ListBox.Visible = False
            Height = Height - ClientSize.Height + PalSearch.Height + 20
        End If
        If CType(ConfigData("setting")("showall"), Boolean) And TxtSearch.Text = "" Then
            AllList()
        End If
        Visible = True
        Activate()
        TxtSearch.Focus()
    End Sub

    Public Sub MenuSetting_Click(sender As Object, e As EventArgs) Handles MenuSetting.Click
        FrmSetting.Show()
    End Sub

    Public Sub MenuExit_Click(sender As Object, e As EventArgs) Handles MenuExit.Click
        NotifyIcon.Dispose()
        UnRegisterHotKey(Handle, 0)
        Application.Exit()
    End Sub

    Private Sub TxtSearch_TextChanged(sender As Object, e As EventArgs) Handles TxtSearch.TextChanged
        If TxtSearch.Text = "" Then
            If CType(ConfigData("setting")("showall"), Boolean) Then
                AllList()
            Else
                ListBox.Visible = False
                Height = Height - ClientSize.Height + PalSearch.Height + 20
            End If
            PicClean.Visible = False
        Else
            PicClean.Visible = True
            PicClean.BackgroundImage = My.Resources.picClean1
            Search()
        End If
    End Sub

    Private Sub TxtSearch_KeyDown(sender As Object, e As KeyEventArgs) Handles TxtSearch.KeyDown
        If e.KeyCode = Keys.Escape Then
            Visible = False
        ElseIf e.KeyCode = Keys.Enter Then
            If ListBox.Items.Count > 0 Then
                Exec()
            End If
        ElseIf e.KeyCode = Keys.Up Then
            If ListBox.Items.Count > 0 Then
                If ListBox.SelectedIndex = 0 Then
                    ListBox.SelectedIndex = ListBox.Items.Count - 1
                Else
                    ListBox.SelectedIndex -= 1
                End If
            End If
            e.Handled = True '阻止光标移动
        ElseIf e.KeyCode = Keys.Down Then
            If ListBox.Items.Count > 0 Then
                If ListBox.SelectedIndex = ListBox.Items.Count - 1 Then
                    ListBox.SelectedIndex = 0
                Else
                    ListBox.SelectedIndex += 1
                End If
            End If
            e.Handled = True '阻止光标移动
        End If
    End Sub

    Private Sub TxtSearch_KeyPress(sender As Object, e As KeyPressEventArgs) Handles TxtSearch.KeyPress
        If e.KeyChar = Chr(27) Or e.KeyChar = Chr(13) Then 'ESC键的Asii码为27，回车键的Asii码为13
            e.Handled = True '取消按下ESC键和回车键的警告音
        End If
    End Sub

    Private Sub NotifyIcon_MouseDoubleClick(sender As Object, e As MouseEventArgs) Handles NotifyIcon.MouseDoubleClick
        MenuOpenWindow_Click(Me, New EventArgs)
    End Sub

    Protected Overrides Sub WndProc(ByRef m As Message)
        If m.Msg = WM_HOTKEY Then
            If Visible = True Then
                Visible = False
            Else
                MenuOpenWindow_Click(Me, New EventArgs)
            End If
        End If
        MyBase.WndProc(m)
    End Sub

    Private Sub ListBox_DrawItem(sender As Object, e As DrawItemEventArgs) Handles ListBox.DrawItem
        If (e.State And DrawItemState.Selected) = DrawItemState.Selected Then
            e = New DrawItemEventArgs(e.Graphics, e.Font, e.Bounds, e.Index, e.State Xor DrawItemState.Selected, Themes.SelectedListTitle, Themes.SelectedBackColor)
            e.DrawBackground()
            'e.DrawFocusRectangle()
            Dim rc As RectangleF = New RectangleF(e.Bounds.X + 32 + 10, e.Bounds.Y + 2, e.Bounds.Width - 32 - 10 - 5, 21)
            e.Graphics.DrawString(ListData(e.Index).Title, New Font(FontFamily, 12, FontStyle.Bold), New SolidBrush(Themes.SelectedListTitle), rc, New StringFormat)
            Dim rc1 As RectangleF = New RectangleF(e.Bounds.X + 32 + 10, e.Bounds.Y + 23, e.Bounds.Width - 32 - 10 - 5, 17)
            e.Graphics.DrawString(ListData(e.Index).Content, New Font(FontFamily, 9, FontStyle.Regular), New SolidBrush(Themes.SelectedListDescription), rc1, New StringFormat)
        Else
            e.DrawBackground()
            'e.DrawFocusRectangle()
            Dim rc As RectangleF = New RectangleF(e.Bounds.X + 32 + 10, e.Bounds.Y + 2, e.Bounds.Width - 32 - 10 - 5, 21)
            e.Graphics.DrawString(ListData(e.Index).Title, New Font(FontFamily, 12, FontStyle.Bold), New SolidBrush(Themes.ListTitle), rc, New StringFormat)
            Dim rc1 As RectangleF = New RectangleF(e.Bounds.X + 32 + 10, e.Bounds.Y + 23, e.Bounds.Width - 32 - 10 - 5, 17)
            e.Graphics.DrawString(ListData(e.Index).Content, New Font(FontFamily, 9, FontStyle.Regular), New SolidBrush(Themes.ListDescription), rc1, New StringFormat)
        End If
        If ListData(e.Index).Icon Is Nothing = False Then
            Dim sz As SizeF
            sz = ListData(e.Index).Icon.PhysicalDimension()
            e.Graphics.DrawImage(ListData(e.Index).Icon, e.Bounds.X + 5, e.Bounds.Y + 5, 32, 32)
        End If
    End Sub

    Private Sub ListBox_MeasureItem(sender As Object, e As MeasureItemEventArgs) Handles ListBox.MeasureItem
        e.ItemHeight = ItemHeight
    End Sub

    Private Sub ListBox_MouseDown(sender As Object, e As MouseEventArgs) Handles ListBox.MouseDown
        If ListBox.SelectedIndex <> -1 Then
            Exec()
        End If
    End Sub

    'Private Sub ListBox_MouseMove(sender As Object, e As MouseEventArgs) Handles ListBox.MouseMove
    '    ListBox.SelectedIndex = ListBox.IndexFromPoint(e.Location)
    'End Sub

    Public Function ListAdd(Optional ListTitle As String = "", Optional ListIcon As Image = Nothing, Optional ListContent As String = "") As Integer
        ListData(ListNum).Icon = ListIcon
        ListData(ListNum).Title = ListTitle
        ListData(ListNum).Content = ListContent
        ListNum += 1
        ReDim Preserve ListData(ListNum)
        ListBox.Items.Insert(ListBox.Items.Count, ListTitle)
        Return ListNum
    End Function

    Public Sub ListClear()
        ListBox.Items.Clear()
        ListNum = 0
        ReDim ListData(ListNum)
    End Sub

    Private Sub AllList()
        JsonList.Clear()
        JsonList = ConfigData("list").DeepClone
        ReDim JsonIndex(JsonList.Count - 1)
        Dim i As Integer
        If JsonIndex.Count > 0 Then
            For i = 0 To JsonIndex.Count - 1
                JsonIndex(i) = i
            Next
        End If
        PrintList()
    End Sub

    Private Sub Search()
        Dim i As Integer, j As Integer
        Dim strLabel As String

        JsonList.Clear()
        ReDim JsonIndex(0)
        For i = 0 To ConfigData("list").Count - 1
            For j = 0 To ConfigData("list")(i)("label").Count - 1
                strLabel = LCase(ConfigData("list")(i)("label")(j).ToString)
                If j = 0 Then
                    Dim k As Integer
                    Dim a() As String
                    a = strLabel.Split("|")
                    If a.Count > 0 Then
                        Dim f As Boolean = False
                        For k = 0 To a.Count - 1
                            If a(k).IndexOf(LCase(TxtSearch.Text)) <> -1 Then
                                JsonList.Add(ConfigData("list")(i))
                                ReDim Preserve JsonIndex(JsonList.Count - 1)
                                JsonIndex(JsonList.Count - 1) = i
                                f = True
                                Exit For
                            End If
                        Next
                        If f Then Exit For
                    End If
                Else
                    If strLabel.IndexOf(LCase(TxtSearch.Text)) <> -1 Then
                        JsonList.Add(ConfigData("list")(i))
                        ReDim Preserve JsonIndex(JsonList.Count - 1)
                        JsonIndex(JsonList.Count - 1) = i
                        Exit For
                    End If
                End If
            Next
        Next

        PrintList()
    End Sub

    Private Sub PrintList()
        Dim i As Integer
        ListClear()
        If JsonList.Count > 0 Then
            If ConfigData("setting")("sort").ToString = "1" Then
                ListSort(JsonList, JsonIndex)
            End If
            For i = 0 To JsonList.Count - 1
                Dim picIcon As Image
                Dim IconPath As String = Application.StartupPath + "\Icon\" + JsonList(i)("icon").ToString
                If JsonList(i)("icon").ToString <> "" And IO.File.Exists(IconPath) Then
                    picIcon = LoadImage(Application.StartupPath + "\Icon\" + JsonList(i)("icon").ToString)
                Else
                    picIcon = CmdList.GetIcon(JsonList(i)("command").ToString, JsonList(i)("type").ToString)
                End If
                If CmdList.IsCommand(JsonList(i)("command").ToString) Then
                    ListAdd(JsonList(i)("name").ToString, picIcon, JsonList(i)("info"))
                Else
                    ListAdd(JsonList(i)("name").ToString, picIcon, JsonList(i)("command"))
                End If
            Next
            ListBox.Height = ItemHeight * IIf(JsonList.Count > Int(ConfigData("setting")("number")), Int(ConfigData("setting")("number")), JsonList.Count)
            Height = Height - ClientSize.Height + PalSearch.Height + ListBox.Height + 30
            ListBox.Visible = True
            ListBox.SelectedIndex = 0
        Else
            Height = Height - ClientSize.Height + PalSearch.Height + 20
        End If
    End Sub

    Private Sub Exec()
        Dim StrCmd = JsonList(ListBox.SelectedIndex)("command").ToString
        Visible = False
        If CmdList.IsCommand(StrCmd) Then
            If CType(ConfigData("setting")("alert"), Boolean) Then
                If StrCmd = "HALT" Or
                StrCmd = "REBOOT" Or
                StrCmd = "HIBERNATE" Or
                StrCmd = "LOGOUT" Then
                    If MsgBox("确定要执行" + JsonList(ListBox.SelectedIndex)("name").ToString + "操作吗？", vbQuestion + vbYesNo, "询问") <> vbYes Then
                        Exit Sub
                    End If
                End If
            End If
            CmdList.RunCmd(StrCmd)
        Else
            Dim t As String = JsonList(ListBox.SelectedIndex)("type").ToString
            If t = "0" Then
                '
            ElseIf t = "1" Then
                '
            ElseIf t = "2" Then
                Process.Start(StrCmd)
            ElseIf t = "3" Then
                Process.Start(StrCmd)
            ElseIf t = "4" Then
                Dim pInfo As New ProcessStartInfo With {
                    .FileName = GetCmd(StrCmd),
                    .WorkingDirectory = IO.Path.GetDirectoryName(.FileName),
                    .Arguments = GetArgs(StrCmd)
                }
                Process.Start(pInfo)
            ElseIf t = "5" Then
                Process.Start(StrCmd)
            End If
        End If
        If ConfigData("setting")("sort").ToString = "1" Then
            ConfigData("list")(JsonIndex(ListBox.SelectedIndex))("count") = CInt(ConfigData("list")(JsonIndex(ListBox.SelectedIndex))("count")) + 1
        ElseIf ConfigData("setting")("sort").ToString = "2" Then
            ListSort(ConfigData("list"), JsonIndex(ListBox.SelectedIndex))
        End If
        IO.File.WriteAllText(ConfigPath, JsonInfo + ConfigData.ToString, Encoding.UTF8)
    End Sub

    Private Sub ListSort(ByRef JsonArray As JArray, ByRef JsonIndex() As Integer) '按使用频率排序
        Dim i As Integer, j As Integer, tt As Integer
        Dim t As JObject
        Dim f As Boolean

        If JsonArray.Count < 2 Then Exit Sub
        For i = 0 To JsonArray.Count - 2
            f = False
            For j = 0 To JsonArray.Count - 2 - i
                If CInt(JsonArray(j)("count")) < CInt(JsonArray(j + 1)("count")) Then
                    t = JsonArray(j)
                    JsonArray(j) = JsonArray(j + 1)
                    JsonArray(j + 1) = t
                    tt = JsonIndex(j)
                    JsonIndex(j) = JsonIndex(j + 1)
                    JsonIndex(j + 1) = tt
                    f = True
                End If
            Next
            If f = False Then Exit For
        Next
    End Sub

    Private Sub ListSort(ByRef JsonArray As JArray, JsonIndex As Integer) '按使用顺序排序
        Dim i As Integer
        Dim t As JObject

        If JsonArray.Count < 2 Or JsonIndex = 0 Then Exit Sub
        t = JsonArray(JsonIndex)
        For i = JsonIndex To 1 Step -1
            JsonArray(i) = JsonArray(i - 1)
        Next
        JsonArray(0) = t
    End Sub

    Public Sub SetThemes()
        If ConfigData("setting")("themes") = "dark" Then
            Themes.SetThemes("dark")
        Else
            Themes.SetThemes("light")
        End If
        BackColor = Themes.Background
        ListBox.BackColor = Themes.ListBackColor
        PalSearch.BackColor = Themes.TextBoxBackColor
        TxtSearch.BackColor = Themes.TextBoxBackColor
        PicClean.BackColor = Themes.TextBoxBackColor
        PicIcon.BackColor = Themes.TextBoxBackColor
        TxtSearch.ForeColor = Themes.TextBoxForeColor
    End Sub
End Class
